package com.macrolab.secure;

import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.x9.ECNamedCurveTable;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMDecryptorProvider;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.*;
import java.util.Base64;

/**
 * 通过 SoftHSM 或 BC 使用 RSA
 *
 * @author macroLu 2019-5-9
 */
public class RSAUtils {

//    static {
//        Security.addProvider(new BouncyCastleProvider());
//    }

    private static final String ALGORITHM_RSA = "RSA";
    public static final String SHA256withRSA = "SHA256withRSA";
    public static final String SHA384withRSA = "SHA384withRSA";
    public static final String SHA512withRSA = "SHA512withRSA";

    public static final int RSA_KEY_SIZE_1024 = 1024;
    public static final int RSA_KEY_SIZE_2048 = 2048;


//    private static final String src = "abcdefghijklmnopqrstuvwxyz";

//    public static void main(String[] args) throws Exception {
//        System.out.println("\n");
//        RSAKeyPair keyPair = generateKeyPair(RSA_KEY_SIZE_2048);
//        System.out.println("公钥：" + keyPair.getPublicKey());
//        System.out.println("私钥：" + keyPair.getPrivateKey());
//        System.out.println("\n");
//        test1(keyPair, src);
//        System.out.println("\n");
//        test2(keyPair, src);
//        System.out.println("\n");
//    }
//
//    /**
//     * 公钥加密私钥解密
//     */
//    private static void test1(RSAKeyPair keyPair, String source) throws Exception {
//        System.out.println("***************** 公钥加密私钥解密开始 *****************");
//        String text1 = encryptByPublicKey(keyPair.getPublicKey(), source);
//        String text2 = decryptByPrivateKey(keyPair.getPrivateKey(), text1);
//        System.out.println("加密前：" + source);
//        System.out.println("加密后：" + text1);
//        System.out.println("解密后：" + text2);
//        if (source.equals(text2)) {
//            System.out.println("解密字符串和原始字符串一致，解密成功");
//        } else {
//            System.out.println("解密字符串和原始字符串不一致，解密失败");
//        }
//        System.out.println("***************** 公钥加密私钥解密结束 *****************");
//    }
//
//    /**
//     * 私钥加密公钥解密
//     *
//     * @throws Exception
//     */
//    private static void test2(RSAKeyPair keyPair, String source) throws Exception {
//        System.out.println("***************** 私钥加密公钥解密开始 *****************");
//        String text1 = encryptByPrivateKey(keyPair.getPrivateKey(), source);
//        String text2 = decryptByPublicKey(keyPair.getPublicKey(), text1);
//        System.out.println("加密前：" + source);
//        System.out.println("加密后：" + text1);
//        System.out.println("解密后：" + text2);
//        if (source.equals(text2)) {
//            System.out.println("解密字符串和原始字符串一致，解密成功");
//        } else {
//            System.out.println("解密字符串和原始字符串不一致，解密失败");
//        }
//        System.out.println("***************** 私钥加密公钥解密结束 *****************");
//    }


    /**
     * 读取pem文件（私钥），获取秘钥对
     *
     * @param pemFile  秘钥文件
     * @param password pem秘钥文件保护密码
     * @return
     * @throws Exception
     */
    public static KeyPair readKeyPair(String pemFile, String password) throws Exception {
        // don't forget to add the provider
        // Security.addProvider(new BouncyCastleProvider());
        // reads your key file
        PEMParser pemParser = new PEMParser(new FileReader(pemFile));
        Object object = pemParser.readObject();
        JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
        KeyPair kp;
        if (object instanceof PEMEncryptedKeyPair) {
            // Encrypted key - we will use provided password
            PEMEncryptedKeyPair ckp = (PEMEncryptedKeyPair) object;
            // uses the password to decrypt the key
            PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(password.toCharArray());
            kp = converter.getKeyPair(ckp.decryptKeyPair(decProv));
        } else {
            // Unencrypted key - no password needed
            PEMKeyPair ukp = (PEMKeyPair) object;
            kp = converter.getKeyPair(ukp);
        }
        return kp;
    }

    /**
     * 公钥解密
     *
     * @param publicKeyText
     * @param text
     * @return
     * @throws Exception
     */
    public static String decryptByPublicKey(String publicKeyText, String text) throws Exception {
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyText));
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_RSA);
        PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
        Cipher cipher = Cipher.getInstance(ALGORITHM_RSA);
        cipher.init(Cipher.DECRYPT_MODE, publicKey);
        byte[] result = cipher.doFinal(Base64.getDecoder().decode(text));
        return new String(result);
    }

    /**
     * 私钥加密
     *
     * @param privateKeyText
     * @param text
     * @return
     * @throws Exception
     */
    public static String encryptByPrivateKey(String privateKeyText, String text) throws Exception {
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyText));
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_RSA);
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        Cipher cipher = Cipher.getInstance(ALGORITHM_RSA);
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);
        byte[] result = cipher.doFinal(text.getBytes());
        return Base64.getEncoder().encodeToString(result);
    }

    /**
     * 私钥解密
     *
     * @param privateKeyText
     * @param text
     * @return
     * @throws Exception
     */
    public static String decryptByPrivateKey(String privateKeyText, String text) throws Exception {
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyText));
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_RSA);
        PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5);
        Cipher cipher = Cipher.getInstance(ALGORITHM_RSA);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] result = cipher.doFinal(Base64.getDecoder().decode(text));
        return new String(result);
    }

    /**
     * 公钥加密
     *
     * @param publicKeyText
     * @param text
     * @return
     */
    public static String encryptByPublicKey(String publicKeyText, String text) throws Exception {
        X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyText));
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM_RSA);
        PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec2);
        Cipher cipher = Cipher.getInstance(ALGORITHM_RSA);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] result = cipher.doFinal(text.getBytes());
        return Base64.getEncoder().encodeToString(result);
    }

//    /**
//     * 构建RSA密钥对
//     *
//     * @return
//     * @throws NoSuchAlgorithmException
//     */
//    public static RSAKeyPair generateKeyPair() throws NoSuchAlgorithmException {
//        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
//        keyPairGenerator.initialize(1024);
//        KeyPair keyPair = keyPairGenerator.generateKeyPair();
//        RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
//        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
//        String publicKeyString = Base64.getEncoder().encodeToString(rsaPublicKey.getEncoded());
//        String privateKeyString = Base64.getEncoder().encodeToString(rsaPrivateKey.getEncoded());
//        RSAKeyPair rsaKeyPair = new RSAKeyPair(publicKeyString, privateKeyString);
//        return rsaKeyPair;
//    }


    /**
     * 构建RSA密钥对
     *
     * @param keySize 1024 | 2048
     * @return KeyPair
     */
    public static KeyPair generateKeyPair(int keySize) {
        // 为RSA算法创建一个KeyPairGenerator对象
        KeyPairGenerator kpg;
        try {
            kpg = KeyPairGenerator.getInstance(ALGORITHM_RSA);
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalArgumentException("No such algorithm-->[" + ALGORITHM_RSA + "]");
        }
        // 初始化KeyPairGenerator对象,密钥长度
        kpg.initialize(keySize);
        // 生成密匙对
        KeyPair keyPair = kpg.generateKeyPair();
        return keyPair;
    }


    /**
     * RSA密钥对对象
     */
    public static class RSAKeyPair {

        private String publicKey;
        private String privateKey;

        public RSAKeyPair(String publicKey, String privateKey) {
            this.publicKey = publicKey;
            this.privateKey = privateKey;
        }

        public String getPublicKey() {
            return publicKey;
        }

        public String getPrivateKey() {
            return privateKey;
        }

    }

    /**
     * RSA 私钥签名
     *
     * @param privateKey
     * @param content
     * @return
     */
    public static byte[] sign(PrivateKey privateKey, byte[] content, String signAlgorithm) throws InvalidKeyException, SignatureException, NoSuchAlgorithmException, InvalidKeySpecException {
        //处理私钥
        PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(privateKey.getEncoded());
        PrivateKey priKey = KeyFactory.getInstance(ALGORITHM_RSA).generatePrivate(priPKCS8);
        //加密
        Signature signature = Signature.getInstance(signAlgorithm);
        signature.initSign(priKey);
        signature.update(content);
        return signature.sign();
    }

    /**
     * RSA 公钥验签
     *
     * @param publicKey     公钥
     * @param data          签名的报文内容
     * @param sign          签名
     * @param signAlgorithm 签名算法: SHA256withRSA
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     * @throws InvalidKeyException
     * @throws SignatureException
     */
    public static boolean verify(PublicKey publicKey, byte[] data, byte[] sign, String signAlgorithm) throws InvalidKeyException, SignatureException, NoSuchAlgorithmException, InvalidKeySpecException {
        byte[] keyBytes = publicKey.getEncoded();
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = null;
        keyFactory = KeyFactory.getInstance(ALGORITHM_RSA);
        PublicKey key = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance(signAlgorithm);
        signature.initVerify(key);
        signature.update(data);
        return signature.verify(sign);

    }


}
